package servlet;

import config.Action;
import config.ActionMapping;
import config.Forward;
import config.Test;
import exception.ActionNotFoundException;
import exception.ConfigErrorException;
import exception.ForwardNotFoundException;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.xmlrules.DigesterLoader;
import org.xml.sax.SAXException;
import parser.ClassBean;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 石小俊
 * @date 2023年07月13日 10:12
 */
@WebServlet(urlPatterns = "*.do",loadOnStartup = 1,initParams = {
        @WebInitParam(name = "configLocation",value = "actions.xml")
})
public class ActionServlet extends HttpServlet {

    private ActionMapping actionMapping;
    private Map<String,Object> actions;

    @Override
    public void init() throws ServletException {
        actions = new HashMap<>();
        actionMapping = new ActionMapping();
        Digester digester = DigesterLoader.createDigester(Test.class.getClassLoader().getResource("config/rule.xml"));
        digester.push(actionMapping);

        try {
            String configLocation = getInitParameter("configLocation");
            digester.parse(Test.class.getClassLoader().getResourceAsStream(configLocation));
        } catch (IOException e) {
            e.printStackTrace();
            throw new ConfigErrorException("配置文件初始化失败");
        } catch (SAXException e) {
            e.printStackTrace();
            throw new ConfigErrorException("配置文件初始化失败");
        }
    }

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println(actionMapping);
        // 获取当前发送的请求
        String path = request.getServletPath();
        // 由于所有请求访问时以.do结尾
        // 但是在配置文件中,其请求没有任何后缀
        // 因此,在解析请求时,需要将请求的后缀截取掉
        path = path.substring(0,path.lastIndexOf("."));
        System.out.println("path:"+path);

        // 此时可以根据请求去配置文件中找到对应的配置
        // 即:根据path找到对应的Action对象
        Action action = actionMapping.findAction(path);
        System.out.println("action:"+action);
        if(action == null){
            throw new ActionNotFoundException("请求"+path+"在配置文件中尚未配置");
        }

        try {
            // 如果找到了对应的action,则根据action的属性找到对应的类
            // Object obj = Class.forName(action.getType()).newInstance();
            Class actionClass = Class.forName(action.getType());
            // 在配置文件中,某些配置可能会共用同一个类
            // 如果每次都创建新的对象,则效率太低
            // 因此,我们可以将其做成单例的
            // 可以将每次创建的对象存放到一个Map集合中
            // 集合的key为当前type属性的值,集合的值即为对应的对象
            // 每次需要创建对象的时候,先去集合中进行查找
            // 如果集合中已经存在,则直接使用
            Object obj = actions.get(action.getType());
            if(obj == null){
                // 如果集合中不存在,则创建一个新的对象
                obj = actionClass.newInstance();
                // 并将创建的对象存放到集合中
                actions.put(action.getType(),obj);
            }

            // 对象获取到之后,我们需要访问的是该对象中的某一个方法
            // 根据parameter配置找到对应的方法
            // 由于在处理请求与响应时,需要用到请求对象与响应对象
            // 因此,Action中对应的方法参数必须包含请求对象与响应对象
            Method method = actionClass.getMethod(action.getParameter(),HttpServletRequest.class,HttpServletResponse.class);
            // 获取目标方法的返回值
            String result = (String) method.invoke(obj,request,response);
            System.out.println("方法返回值:"+result);
            if(result == null){
                System.out.println("当前方法无返回值,请使用Ajax来实现");
                return;
            }

            // 根据方法返回值找到对应的Forward配置
            Forward forward = action.findForward(result);
            System.out.println("forward:"+forward);
            if(forward == null){
                throw new ForwardNotFoundException("根据返回值"+result+"在配置文件中没有找到对应配置");
            }

            // 找到Forward配置之后,根据redirect属性判断是否使用重定向
            if(forward.getRedirect()){
                // 值为true,则使用重定向
                // 通过重定向跳转到path所对应的页面
                response.sendRedirect(request.getContextPath()+forward.getPath());
                return;
            }
            // 否则使用转发进行跳转
            request.getRequestDispatcher(forward.getPath()).forward(request,response);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServletException(e.getMessage());
        }
    }
}
